from PyQt5.QtCore import QTimer, pyqtSignal, Qt, QThread, QMutex
import resources.resource_rc
from serialPort import SerialPortWindow
from serialPort import cm3_pb2
from windowUI import *

qmutSaveLog = QMutex()  # 创建线程锁


# 继承QThread
class SaveLogRunthread(QThread):
    # 创建指针
    logsRecorder = object()

    def __init__(self):
        super(SaveLogRunthread, self).__init__()

    def __del__(self):
        try:
            self.wait()
        except Exception as err:
            pass

    def run(self):
        # print('SaveLogRunthread 1')
        qmutSaveLog.lock()  # 加锁
        # print('SaveLogRunthread 2')
        try:
            # print('SaveLogRunthread 3')
            self.logsRecorder.SaveAllRecorder()
            # print('SaveLogRunthread 4')
            qmutSaveLog.unlock()  # 解锁
        except Exception as err:
            qmutSaveLog.unlock()  # 解锁
            print('Save Log Runthread ERROR :', err)


class MainWindow(QWidget):
    # 声明带一个字典类型参数的信号
    mainSignal = pyqtSignal(dict)  # 主界面的信号用来绑定子界面类的函数方法

    def __init__(self):
        super().__init__()
        # 引入公共程序
        from utils.common import commonProgram
        # 引入配置文件
        self.myConfig = commonProgram.EnableConfigHandler()
        print('MainWindow get config Version', self.myConfig.version)
        # 更新显示器分辨率
        commonProgram.UpdateScreenResolution()
        # 引入可控串口信息窗口
        commonProgram.EnableSerialPort(winSignal=self.mainSignal, RecvSerialData=self.RecvSerialData)
        # 引入测试记录模块
        self.logsRecorder = commonProgram.EnableLogsRecorder(file_name='index')
        # 初始化测试项
        self.InitTestItems()
        # print(cm3_pb2.ItemIndex.items())
        # print(cm3_pb2.ItemIndex.values())
        # print(cm3_pb2.ItemIndex.Value('ViewParam'))
        # print(cm3_pb2.ItemIndex.keys())
        # print(cm3_pb2.ItemIndex.Name(0))

        # [('ViewParam', 0), ('PicturesPlay', 1), ('TpDrawLine', 2), ('TouchKey', 3), ('Gyro', 4)]
        # [0, 1, 2, 3, 4]
        # 0
        # ['ViewParam', 'PicturesPlay', 'TpDrawLine', 'TouchKey', 'Gyro']
        # ViewParam
        # 定时保存测试记录，周期为 XXXms
        self.timer_logsRecorder_delay = self.myConfig.logsRecorderTime
        self.timer_logsRecorder = QTimer()
        self.timer_logsRecorder.timeout.connect(self.LogsRecorderFun)
        self.timer_logsRecorder.start(self.timer_logsRecorder_delay)

    def LogsRecorderFun(self):
        self.timer_logsRecorder.stop()
        # 测试记录，更新测试通过项
        for test in self.logsRecorder.testList:
            for item in self.myConfig.passTestItems:
                if test.get('compName', 'x_x') in item:
                    test.update({"result": True})
                    break
        # 创建线程
        self.saveLogRunthread = SaveLogRunthread()
        # 连接信号
        self.saveLogRunthread.logsRecorder = self.logsRecorder
        # 开始线程
        self.saveLogRunthread.start()
        self.timer_logsRecorder.start(self.timer_logsRecorder_delay)

    def InitTestItems(self):
        self.myConfig.passTestItems = set()
        self.logsRecorder.testList = []
        idx = -1
        if not isinstance(self.myConfig.testItems, list):
            print('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
            print('X- There are no test items. Please confirm the configuration file -X')
            print('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
            msg_box = QMessageBox(QMessageBox.Critical, 'testItems Error',
                                  'There are no test items. Please confirm the configuration file !!\n没有发现测试项，请在配置文件确认！！')
            msg_box.exec_()
            return
        for item in self.myConfig.testItems:
            compName = item['compName']
            btnName = item['btnName']
            idx += 1
            testDict = {"id": idx, "btnName": item['btnName'], "compName": item['compName'],
                        "result": False}
            self.logsRecorder.testList.append(testDict)
            # 初始化窗口
            # if 'ViewParam' in compName:
            #     continue
            selected_module = importlib.import_module(f"windowUI.{compName[0].lower() + compName[1:]}Window")
            importlib.reload(selected_module)
            expr = f"self.myConfig.{compName}Win = selected_module.{compName}Window()"
            exec(expr)
            expr = f"self.myConfig.{compName}Win.close()"
            exec(expr)
            # 设置窗口名
            expr = f"self.myConfig.{compName}Win.setWindowTitle('{self.myConfig.APPName} ---- {btnName}')"
            exec(expr)
            if idx <= 0:
                # 显示窗口
                expr = f"self.myConfig.{compName}Win.show()"
                exec(expr)
                # 窗口全屏
                expr = f"self.myConfig.{compName}Win.showFullScreen()"
                exec(expr)

    def CleanRecordsFun(self):
        self.InitTestItems()
        # print('CleanRecordsFun')

    def SendResultsFun(self):
        self.allPassStr = ''
        for item in self.myConfig.passTestItems:
            if len(self.allPassStr) <= 0:
                expr1 = f"self.allPassStr = str(cm3_pb2.{item}) + ','"
                exec(expr1)
            else:
                expr2 = f"self.allPassStr = self.allPassStr + str(cm3_pb2.{item}) + ','"
                exec(expr2)
        dictData = {
            'type': 'send_protobuf',
            'data': {
                "msg_type": cm3_pb2.AllItemResult,
                "item": {
                    "index": cm3_pb2.ViewParam,
                    "all_pass": self.allPassStr
                }
            }
        }
        self.DataHandleFun(dictData)

    def SendItemResultFun(self, itemCompName: str):
        compName = itemCompName.strip()
        if not (compName in cm3_pb2.ItemIndex.keys()):
            return
        itemTestResult = cm3_pb2.Fail
        for item in self.myConfig.passTestItems:
            if item in itemCompName:
                itemTestResult = cm3_pb2.Pass
                break
        dictData = {
            'type': 'send_protobuf',
            'data': {
                "msg_type": cm3_pb2.ItemResult,
                "item": {
                    "index": cm3_pb2.ItemIndex.Value(itemCompName.strip()),
                    "result": itemTestResult
                }
            }
        }
        self.DataHandleFun(dictData)

    def ItemChangeFun(self, variate=0, itemIndex=None):
        toItem = None
        for item in self.myConfig.testItems:
            compName = item['compName']
            if (itemIndex != None) and (itemIndex in compName):
                toItem = item
            expr1 = f"self.SendItemResultFun(itemCompName=compName) if self.myConfig.{compName}Win.isVisible() else None"
            exec(expr1)
            expr2 = f"self.myConfig.{compName}Win.close()"
            exec(expr2)
        if (itemIndex != None) and (toItem != None):
            item = toItem
        else:
            self.myConfig.itemIndex += variate
            if (self.myConfig.itemIndex < 0) or (self.myConfig.itemIndex >= len(self.myConfig.testItems)):
                self.myConfig.itemIndex = 0
            item = self.myConfig.testItems[self.myConfig.itemIndex]
        # 显示窗口
        expr2 = f"self.myConfig.{item['compName']}Win.show()"
        exec(expr2)
        # 窗口全屏
        expr3 = f"self.myConfig.{item['compName']}Win.showFullScreen()"
        exec(expr3)
        return item['compName']

    def RecvSerialData(self, dictData: dict):
        # 用于接收Form2发过来的数据
        # dataStr = dictData.get("data", None)  # num2 就是子界面传递过来的数据
        self.DataHandleFun(dictData)

    def DataHandleFun(self, dataDict: dict):
        # print("dataDict", dataDict)
        typeStr = dataDict.get('type', 'null').strip()
        if typeStr == 'send_protobuf':
            self.mainSignal.emit(dataDict)
        elif typeStr == 'rece_protobuf':
            self.HandleProtobufFun(dataDict)
        elif '_to_Main' in typeStr:
            dictData = dataDict.get('data', 'null')
            if dictData['msg_type'].strip() == 'function':
                if (dictData['operate'].strip() == 'call') and (dictData['subject'].strip() == 'Main'):
                    expr = f"self.{dictData['object']}"
                    exec(expr)
            if dictData['msg_type'].strip() == 'logic':
                if (dictData['operate'].strip() == 'switch') and (dictData['subject'].strip() == 'Item'):
                    self.ItemChangeFun(variate=0, itemIndex=dictData['object'].strip())

    def HandleProtobufFun(self, dataDict: dict):
        display = dataDict.get('data', '')
        if display.msg_type == cm3_pb2.Item:
            # print('*' * 6, display.item.index)
            ItemIndex = cm3_pb2.ItemIndex.Name(display.item.index)
            if len(ItemIndex) > 0:
                self.ItemChangeFun(variate=0, itemIndex=ItemIndex)
            btnName = ''
            # 测试记录，更新测试通过项
            for test in self.logsRecorder.testList:
                if test.get('compName', 'x_x') in ItemIndex:
                    btnName = test.get('btnName', 'x_x')
                    break
            # 测试记录
            dictData = {
                "index": display.item.index,
                "btnName": btnName,
                "compName": ItemIndex,
            }
            self.logsRecorder.ItemFun(dictData)
        elif display.msg_type == cm3_pb2.AllItemResult:
            self.SendResultsFun()
            # 保存测试记录
            self.LogsRecorderFun()
        elif display.msg_type == cm3_pb2.ItemResult:
            # print(display.item.index)
            compName = cm3_pb2.ItemIndex.Name(display.item.index)
            self.SendItemResultFun(itemCompName=compName)
        elif display.msg_type == cm3_pb2.Input:
            # if self.myConfig.itemLock >= 1:
            #     print('item do it!')
            #     return
            # 测试记录
            dictData = {
                "key": display.input.key,
                "rot": display.input.rot
            }
            if display.input.key == 2:
                # print('back')
                dictData = {
                    "key": display.input.key,
                    "rot": display.input.rot,
                    'type': '测试板按键'
                }
                self.ItemChangeFun(variate=-1)
            elif display.input.key == 3:
                dictData = {
                    "key": display.input.key,
                    "rot": display.input.rot,
                    'type': '测试板按键'
                }
                # print('failure')
            elif display.input.key == 4:
                # print('forward')
                dictData = {
                    "key": display.input.key,
                    "rot": display.input.rot,
                    'type': '测试板按键'
                }
                self.ItemChangeFun(variate=1)
            else:
                #配置文件 没有的话，后台输出错误信息，界面无提示，程序无影响
                expr = f"self.touchKeyItems = self.myConfig.touchKeyItems.tou{self.myConfig.screenResolution}"
                exec(expr)
                for item in self.touchKeyItems:
                    if item['index'] == display.input.key:
                        # 测试记录
                        dictData = {
                            "key": display.input.key,
                            "rot": display.input.rot,
                            'type': 'touch key: ' + item['name']
                        }
                        break
            self.logsRecorder.InputFun(dictData)
        elif display.msg_type == cm3_pb2.Status:
            # 测试记录
            self.logsRecorder.StatusFun(display.status)
        elif display.msg_type == cm3_pb2.Version:
            # 测试记录
            self.logsRecorder.VersionFun(display.version)
        elif display.msg_type == cm3_pb2.DTC:
            # 测试记录
            self.logsRecorder.DTCFun(display.dtc)


# 程序入口
if __name__ == "__main__":
    ######使用下面的方式一定程度上可以解决界面模糊问题--解决电脑缩放比例问题
    # debain 上面没有这个 ：setHighDpiScaleFactorRoundingPolicy
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    # 在主函数入口之前加入上面的设置即可解决
    # 适应高DPI设备
    # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    # 解决图片在不同分辨率显示模糊问题
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    # QApplication.setAttribute( Qt.AA_Use96Dpi)
    app = QApplication(sys.argv)
    font = QFont("宋体")
    pointsize = font.pointSize()
    font.setPixelSize(int(pointsize * 90 / 72))
    app.setFont(font)
    # myWin = MyMainWindow()
    mainWindow = MainWindow()
    # mainWindow.show()
    sys.exit(app.exec_())
